package com.aqumon.www.service.impl;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.aqumon.www.constants.enums.StatusCode;
import com.aqumon.www.entity.dto.JenkinsBuildInfoDto;
import com.aqumon.www.entity.dto.JenkinsJobBuildDto;
import com.aqumon.www.entity.exception.CaseServerException;
import com.aqumon.www.entity.persistent.*;
import com.aqumon.www.entity.request.jenkins.JobBasicReq;
import com.aqumon.www.entity.request.jenkins.JobBuildReq;
import com.aqumon.www.entity.response.jenkins.BuildRecordResp;
import com.aqumon.www.mapper.JenkinsBuildMapper;
import com.aqumon.www.mapper.JenkinsConfigMapper;
import com.aqumon.www.mapper.ScriptMapper;
import com.aqumon.www.service.JenkinsBuildService;
import com.aqumon.www.util.JenkinsUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.offbytwo.jenkins.JenkinsServer;
import com.offbytwo.jenkins.client.JenkinsHttpClient;
import com.offbytwo.jenkins.model.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * <p>
 * job 部分构建表 服务实现类
 * </p>
 *
 * @author aqumon
 * @since 2022-05-19
 */
@Service
public class JenkinsBuildServiceImpl extends ServiceImpl<JenkinsBuildMapper, JenkinsBuild> implements JenkinsBuildService {

    private static final Logger LOGGER = LoggerFactory.getLogger(JenkinsBuildServiceImpl.class);

    @Resource
    private JenkinsBuildMapper jenkinsBuildMapper;

    @Resource
    private JenkinsConfigMapper jenkinsConfigMapper;

    @Resource
    private ScriptMapper scriptMapper;

    @Override
    public List<BuildRecordResp> getBuildList(Long scriptId) {
        List<BuildRecordResp> res = new ArrayList<>();
        Script script = scriptMapper.selectById(scriptId);
        if (script == null) {
            throw new CaseServerException("脚本不存在", StatusCode.NOT_FOUND_ENTITY);
        }
        Long scriptJenkinsId = script.getJenkinsId();
        if (scriptJenkinsId == null || scriptJenkinsId == -1L) {
            LOGGER.warn("scriptJenkinsId 非法: " + scriptJenkinsId);
        } else {
            JenkinsConfig jenkinsConfig = jenkinsConfigMapper.selectById(script.getJenkinsId());
            String jobFullName = script.getJobFullName();
            JenkinsServer jenkinsServer = JenkinsUtil.connection(jenkinsConfig);
            JenkinsBuild jenkinsBuild = new JenkinsBuild();

            ObjectMapper lastBuildMapper = new ObjectMapper();
            ObjectNode lastBuildNode = lastBuildMapper.createObjectNode();

            ObjectMapper lastSuccessfulBuildMapper = new ObjectMapper();
            ObjectNode lastSuccessfulBuildNode = lastSuccessfulBuildMapper.createObjectNode();

            ObjectMapper lastFailedBuildMapper = new ObjectMapper();
            ObjectNode lastFailedBuildNode = lastFailedBuildMapper.createObjectNode();
            try {
                JobWithDetails job = jenkinsServer.getJob(jobFullName);

                Build lastBuild = job.getLastBuild();
                Build lastSuccessfulBuild = job.getLastSuccessfulBuild();
                Build lastFailedBuild = job.getLastFailedBuild();

                lastBuildNode.put("number", lastBuild.getNumber());
                lastBuildNode.put("url", (lastBuild.getUrl() + "console"));
                lastSuccessfulBuildNode.put("number", lastSuccessfulBuild.getNumber());
                lastSuccessfulBuildNode.put("url", (lastSuccessfulBuild.getUrl() + "console"));
                lastFailedBuildNode.put("number", lastFailedBuild.getNumber());
                lastFailedBuildNode.put("url", (lastFailedBuild.getUrl() + "console"));

                jenkinsBuild.setLastBuild(lastBuildMapper.writeValueAsString(lastBuildNode));
                jenkinsBuild.setLastFailedBuild(lastFailedBuildMapper.writeValueAsString(lastFailedBuildNode));
                jenkinsBuild.setLastSuccessfulBuild(lastSuccessfulBuildMapper.writeValueAsString(lastSuccessfulBuildNode));
                jenkinsBuild.setScriptId(scriptId);
                if (jenkinsBuildMapper.listAllBuild(scriptId).size() == 0) {
                    jenkinsBuild.setGmtCreated(new Date());
                    jenkinsBuildMapper.insertSelective(jenkinsBuild);
                } else {
                    jenkinsBuildMapper.updateBySelective(jenkinsBuild);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        List<JenkinsBuild> jenkinsBuildList = jenkinsBuildMapper.listAllBuild(scriptId);
        for (JenkinsBuild jenkinsBuild : jenkinsBuildList) {
            BuildRecordResp buildRecordResp = new BuildRecordResp();
            buildRecordResp.setScriptId(scriptId);
            buildRecordResp.setLastBuild(jenkinsBuild.getLastBuild());
            buildRecordResp.setLastFailedBuild(jenkinsBuild.getLastFailedBuild());
            buildRecordResp.setLastSuccessfulBuild(jenkinsBuild.getLastSuccessfulBuild());
            res.add(buildRecordResp);
        }
        return res;
    }

    @Override
    public List<JenkinsJobBuildDto> getJobParams(String jenkinsName, String jobFullName) {
        JenkinsConfig jenkinsConfig = jenkinsConfigMapper.selectByJenkinsName(jenkinsName);
        JenkinsHttpClient jenkinsHttpClient = JenkinsUtil.getClient(jenkinsConfig);
        List<JenkinsJobBuildDto> paramList = new ArrayList<>();
        try {
            String xml = jenkinsHttpClient.get(JenkinsUtil.toJobBaseUrl(JenkinsUtil.toFullJobPath(jobFullName)));
            JSONObject jsonConfig = JSONObject.parseObject(xml);
            String strJsonConfig = jsonConfig.toJSONString();
            ObjectNode node = new ObjectMapper().readValue(strJsonConfig, ObjectNode.class);
            JsonNode parameterDefinitions = node.findValue("parameterDefinitions");
            if (parameterDefinitions == null) {
                return null;
            }
            LOGGER.info("parameterDefinitions: " + node.get("parameterDefinitions"));
            for (int i = 0; i < parameterDefinitions.size(); i++) {
                JenkinsJobBuildDto jenkinsJobBuildDto = new JenkinsJobBuildDto();
                JsonNode parameter =parameterDefinitions.get(i);
                jenkinsJobBuildDto.setName(parameter.get("name").toString());
                if (parameter.get("description") != null) {
                    jenkinsJobBuildDto.setDescription(parameter.get("description").toString());
                }
                JsonNode defaultValueObj = parameter.get("defaultParameterValue");
                jenkinsJobBuildDto.setValue(defaultValueObj.get("value").toString());
                paramList.add(jenkinsJobBuildDto);
            }
            System.out.println(paramList);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return paramList;
    }

    @Override
    public JenkinsBuildInfoDto getLastBuildInfo(JenkinsConfig jenkinsConfig, String jobFullName) {
        JenkinsServer jenkinsServer = JenkinsUtil.connection(jenkinsConfig);
        JenkinsBuildInfoDto jenkinsBuildInfoDto = new JenkinsBuildInfoDto();
        try {
            JobWithDetails job = jenkinsServer.getJob(jobFullName);
            Build lastBuild = job.getLastBuild();
            // 如果是新建job项目
            if (lastBuild.getUrl().equals("UNKNOWN")) {
                jenkinsBuildInfoDto.setBuildStatus("新建");
                jenkinsBuildInfoDto.setBuildNumber((long) -1);
                return jenkinsBuildInfoDto;
            } else {
                BuildWithDetails lastBuildDetail = lastBuild.details();
                // 获取构建的显示名称
                jenkinsBuildInfoDto.setDisplayName(lastBuildDetail.getDisplayName());
                // 获取构建编号
                jenkinsBuildInfoDto.setBuildNumber((long) lastBuildDetail.getNumber());
                // 获取构建结果，如果构建未完成则会显示为null
                BuildResult buildResult = lastBuildDetail.getResult();
                if (buildResult == null && lastBuildDetail.isBuilding()) {
                    jenkinsBuildInfoDto.setBuildStatus("Building");
                } else {
                    assert buildResult != null;
                    jenkinsBuildInfoDto.setBuildStatus(buildResult.name());
                }
                // 获取构建持续多少时间(秒)
                jenkinsBuildInfoDto.setDuration(lastBuildDetail.getDuration()/1000);
                // 获取构建开始时间戳，转换成日期时间格式
                DateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                jenkinsBuildInfoDto.setBuildTime(new Date(lastBuildDetail.getTimestamp()));
                // 获取构建头信息，里面包含构建的用户，上游信息，时间戳等
                List<BuildCause> buildCauses = lastBuildDetail.getCauses();
                for (BuildCause bc : buildCauses) {
                    jenkinsBuildInfoDto.setShortDescription(bc.getShortDescription());
                    jenkinsBuildInfoDto.setUserName(bc.getUserName());
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return jenkinsBuildInfoDto;
    }

    @Override
    public JenkinsBuildInfoDto getBuildInfo(String jenkinsName, String jobFullName, Long buildNumber) {
        JenkinsConfig jenkinsConfig = jenkinsConfigMapper.selectByJenkinsName(jenkinsName);
        JenkinsServer jenkinsServer = JenkinsUtil.connection(jenkinsConfig);
        JenkinsBuildInfoDto jenkinsBuildInfoDto = new JenkinsBuildInfoDto();
        try {
            // 获取请求里面的jobName及buildNumber
            JobWithDetails job = jenkinsServer.getJob(jobFullName);
            if (buildNumber == null) {
                if (job.getFirstBuild().getUrl().equals("UNKNOWN")) {
                    jenkinsBuildInfoDto.setBuildStatus("新建");
                    jenkinsBuildInfoDto.setBuildNumber((long) -1);
                    return jenkinsBuildInfoDto;
                } else {
                    BuildWithDetails build = job.getLastBuild().details();
                    // 获取构建的显示名称
                    jenkinsBuildInfoDto.setDisplayName(build.getDisplayName());
                    // 获取构建编号
                    jenkinsBuildInfoDto.setBuildNumber((long) build.getNumber());
                }
            } else {
                Build numberBuild = job.getBuildByNumber(Math.toIntExact(buildNumber));
                BuildWithDetails build = numberBuild.details();
                // 获取构建的显示名称
                jenkinsBuildInfoDto.setDisplayName(build.getDisplayName());
                // 获取构建编号
                jenkinsBuildInfoDto.setBuildNumber((long) build.getNumber());
                // 获取构建结果，如果构建未完成则会显示为null
                BuildResult buildResult = build.getResult();
                if (buildResult == null && build.isBuilding()) {
                    jenkinsBuildInfoDto.setBuildStatus("Building");
                } else {
                    assert buildResult != null;
                    jenkinsBuildInfoDto.setBuildStatus(buildResult.name());
                }
                // 获取执行构建的活动信息
                System.out.println(build.getActions());
                // 获取构建持续多少时间(ms)
                jenkinsBuildInfoDto.setDuration(build.getDuration());
                // 获取构建开始时间戳，转换成日期时间格式
                DateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                jenkinsBuildInfoDto.setBuildTime(new Date(build.getTimestamp()));
                // 获取构建头信息，里面包含构建的用户，上游信息，时间戳等
                List<BuildCause> buildCauses = build.getCauses();
                for (BuildCause bc : buildCauses) {
                    jenkinsBuildInfoDto.setShortDescription(bc.getShortDescription());
                    jenkinsBuildInfoDto.setUserName(bc.getUserName());
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return jenkinsBuildInfoDto;
    }

    @Override
    public String getBuildLog(String jenkinsName, String jobFullName, Long buildNumber) {
        String buildLog = "";
        JenkinsConfig jenkinsConfig = jenkinsConfigMapper.selectByJenkinsName(jenkinsName);
        JenkinsServer jenkinsServer = JenkinsUtil.connection(jenkinsConfig);
        try {
            // 获取请求里面的jobName及buildNumber
            JobWithDetails job = jenkinsServer.getJob(jobFullName);
            if (buildNumber == null) {
                if (job.getFirstBuild().getUrl().equals("UNKNOWN")) {
                    buildLog = "";
                } else {   //buildNumber为空，且不是第一次构建，则获取最近一次构建日志
                    BuildWithDetails build = job.getLastBuild().details();
                    ConsoleLog currentLog = build.getConsoleOutputText(0);
                    buildLog = currentLog.getConsoleLog();
                }
            } else {
                Build numberBuild = job.getBuildByNumber(Math.toIntExact(buildNumber));
                BuildWithDetails build = numberBuild.details();
                // 当前日志
                ConsoleLog currentLog = build.getConsoleOutputText(0);
                // 检测是否还有更多日志,如果是则继续循环获取
                while (currentLog.getHasMoreData()){
                    // 获取最新日志信息
                    ConsoleLog newLog = build.getConsoleOutputText(currentLog.getCurrentBufferSize());
                    // 输出最新日志
                    System.out.println(newLog.getConsoleLog());
                    currentLog = newLog;
                    buildLog = currentLog.getConsoleLog();
                    // 睡眠1s
                    Thread.sleep(1000);
                }
                // 输出当前获取日志信息
                System.out.println(buildLog);
            }
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }
        return buildLog;
    }

    @Override
    public String stopLastJobBuild(JobBasicReq jobBasicReq) {
        JenkinsConfig jenkinsConfig = jenkinsConfigMapper.selectByJenkinsName(jobBasicReq.getJenkinsName());
        JenkinsServer jenkinsServer = JenkinsUtil.connection(jenkinsConfig);
        try {
            // 获取最后的 build 信息
            Build build = jenkinsServer.getJob(jobBasicReq.getJobFullName()).getLastBuild();
            // 停止最后的 build
            build.Stop();
            return "停止构建成功";
        } catch (IOException e) {
            e.printStackTrace();
        }
        return "";
    }

    @Override
    public boolean buildJob(JobBuildReq jobBuildReq) {
        if (jobBuildReq.getJobParams() == null) {
            return buildJobNoParams(jobBuildReq);
        } else if (jobBuildReq.getJobParams() != null) {
            return buildJobWithParams(jobBuildReq);
        }
        return true;
    }

    /**
     * 执行无参数 Job build
     */
    private boolean buildJobNoParams(JobBuildReq jobBuildReq){
        String jobName = jobBuildReq.getJobFullName();
        JenkinsConfig jenkinsConfig = jenkinsConfigMapper.selectByJenkinsName(jobBuildReq.getJenkinsName());
        JenkinsServer jenkinsServer = JenkinsUtil.connection(jenkinsConfig);
        try {
            jenkinsServer.getJob(jobName).build(true);
        } catch (IOException e) {
            LOGGER.error(null, e);
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 执行带参数 Job build
     */
    private boolean buildJobWithParams(JobBuildReq jobBuildReq){
        String jobName = jobBuildReq.getJobFullName();
        String strParams = jobBuildReq.getJobParams();
        JenkinsConfig jenkinsConfig = jenkinsConfigMapper.selectByJenkinsName(jobBuildReq.getJenkinsName());

//        String strParams = "{\"name\": \"jmeter\", \"thread\": \"1\"}";
        JSONObject objParams = JSONObject.parseObject(strParams);
        Map<String,String> mapParams = new HashMap<>();
        Set<String> keySet = objParams.keySet();
        for (String key : keySet) {
            String value = objParams.getString(key);
            mapParams.put(key, value);
        }
        LOGGER.info("构建map参数 => " + mapParams);
        JenkinsServer jenkinsServer = JenkinsUtil.connection(jenkinsConfig);
        try {
            jenkinsServer.getJob(jobName).build(mapParams, true);
        } catch (IOException e) {
            LOGGER.error(null, e);
            e.printStackTrace();
            return false;
        }
        return true;
    }

    @Override
    public boolean disableJob(JobBasicReq jobBasicReq) {
        String jobName = jobBasicReq.getJobFullName();
        JenkinsConfig jenkinsConfig = jenkinsConfigMapper.selectByJenkinsName(jobBasicReq.getJenkinsName());
        JenkinsServer jenkinsServer = JenkinsUtil.connection(jenkinsConfig);
        try {
            jenkinsServer.disableJob(jobName);
            return true;
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
    }

    @Override
    public boolean enableJob(JobBasicReq jobBasicReq) {
        String jobName = jobBasicReq.getJobFullName();
        JenkinsConfig jenkinsConfig = jenkinsConfigMapper.selectByJenkinsName(jobBasicReq.getJenkinsName());
        JenkinsServer jenkinsServer = JenkinsUtil.connection(jenkinsConfig);
        try {
            jenkinsServer.enableJob(jobName);
        } catch (IOException e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }
}
